#!/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby -W0

$:.unshift Dir["/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/ruby/{1.8,2.0.0}"].first
require 'pathname'
require 'set'
require 'stringio'

class Logger
  def initialize
    @io = StringIO.new
  end

  def puts(*args)
    @io.puts(*args)
  end

  def log!
    return unless ENV.key? 'HOMEBREW_CC_LOG_PATH'
    path = "#{ENV['HOMEBREW_CC_LOG_PATH']}.cc"

    puts
    File.open(path, File::WRONLY | File::APPEND | File::CREAT) { |f| f.write(@io.string) }
  end
end

LOGGER = Logger.new

class Cmd
  attr_reader :brewfix, :brewtmp, :sysroot

  def initialize path, args
    @arg0 = File.basename(path).freeze
    @args = args.freeze
    @brewfix = ENV['HOMEBREW_PREFIX']
    @brewtmp = ENV['HOMEBREW_TEMP']
    @sysroot = ENV['HOMEBREW_SDKROOT']
  end

  def mode
    if @arg0 == 'cpp' or @arg0 == 'ld'
      @arg0.to_sym
    elsif @args.include? '-c'
      if @arg0 =~ /(?:c|g|clang)\+\+/
        :cxx
      else
        :cc
      end
    elsif @args.include? '-E'
      :ccE
    else
      if @arg0 =~ /(?:c|g|clang)\+\+/
        :cxxld
      else
        :ccld
      end
    end
  end

  def tool
    @tool ||= case @arg0
    when 'ld' then 'ld'
    when 'cpp' then 'cpp'
    when /\w\+\+(-\d\.\d)?$/
      case ENV['HOMEBREW_CC']
      when /clang/
        'clang++'
      when /llvm-gcc/
        'llvm-g++-4.2'
      when /gcc(-\d\.\d)?$/
        'g++' + $1.to_s
      end
    else
      # Note that this is a universal fallback, so that we'll always invoke
      # HOMEBREW_CC regardless of what name under which the tool was invoked.
      ENV['HOMEBREW_CC']
    end
  end

  def args
    if @args.length == 1 and @args[0] == '-v'
      # Don't add linker arguments if -v passed as sole option. This stops gcc
      # -v with no other arguments from outputting a linker error. Some
      # software uses gcc -v (wrongly) to sniff the GCC version.
      return @args.dup
    end
    if !cccfg?("O") || tool == "ld" || configure?
      args = @args.dup
    else
      args = refurbished_args
    end

    if sysroot
      if tool == "ld"
        args << "-syslibroot" << sysroot
      else
        args << "-isysroot" << sysroot << "--sysroot=#{sysroot}"
      end
    end

    allflags = case mode
    when :ccld
      cflags + args + cppflags + ldflags
    when :cxxld
      cxxflags + args + cppflags + ldflags
    when :cc
      cflags + args + cppflags
    when :cxx
      cxxflags + args + cppflags
    when :ccE
      args + cppflags
    when :cpp
      args + cppflags
    when :ld
      ldflags + args
    end.compact
    make_fuss(allflags)
    allflags
  end

  def refurbished_args
    @lset = Set.new(library_paths + system_library_paths)
    @iset = Set.new(isystem_paths + include_paths)

    args = []
    enum = @args.each

    loop do
      case arg = enum.next
      when "-arch"
        if cccfg?("K")
          args << arg << enum.next
        else
          enum.next
        end
      when "-m32", "-m64"
        args << arg if cccfg?("K")
      when /^-Xarch_/
        refurbished = refurbish_arg(enum.next, enum)
        unless refurbished.empty?
          args << arg
          args += refurbished
        end
      else
        args += refurbish_arg(arg, enum)
      end
    end

    args
  end

  def refurbish_arg(arg, enum)
    args = []

    case arg
    when /^-g\d?/, /^-gstabs\d+/, '-gstabs+', /^-ggdb\d?/, '-gdwarf-2',
      /^-march=.+/, /^-mtune=.+/, /^-mcpu=.+/,
      /^-O[0-9zs]?$/, '-fast', '-no-cpp-precomp',
      '-pedantic', '-pedantic-errors', '-Wno-long-double'
    when '-fopenmp', '-lgomp', '-mno-fused-madd', '-fforce-addr', '-fno-defer-pop',
      '-mno-dynamic-no-pic', '-fearly-inlining', /^-f(?:no-)?inline-functions-called-once/,
      /^-finline-limit/, /^-f(?:no-)?check-new/, '-fno-delete-null-pointer-checks',
      '-fcaller-saves', '-fthread-jumps', '-fno-reorder-blocks', '-fcse-skip-blocks',
      '-frerun-cse-after-loop', '-frerun-loop-opt', '-fcse-follow-jumps',
      '-fno-regmove', '-fno-for-scope', '-fno-tree-pre', '-fno-tree-dominator-opts',
      '-fuse-linker-plugin'
      # clang doesn't support these flags
      args << arg if not tool =~ /^clang/
    when /^-W[alp],/, /^-Wno-/
      args << arg
    when /^-W.*/
      # prune warnings
    when '-macosx_version_min', '-dylib_install_name'
      args << "-Wl,#{arg},#{enum.next}"
    when '-multiply_definedsuppress'
      args << "-Wl,-multiply_defined,suppress"
    when '-undefineddynamic_lookup'
      args << "-Wl,-undefined,dynamic_lookup"
    when /^-isysroot/, /^--sysroot/
      # We set the sysroot
      enum.next
    when '-dylib'
      args << "-Wl,#{arg}"
    when /^-I(.+)?/
      # Support both "-Ifoo" (one argument) and "-I foo" (two arguments)
      val  = chuzzle($1) || enum.next
      path = canonical_path(val)
      args << "-I#{val}" if keep?(path) && @iset.add?(path)
    when /^-L(.+)?/
      val  = chuzzle($1) || enum.next
      path = canonical_path(val)
      args << "-L#{val}" if keep?(path) && @lset.add?(path)
    else
      args << arg
    end

    args
  end

  def keep? path
    case path
    when %r{^#{Regexp.escape(brewfix)}}o, %r{^#{Regexp.escape(brewtmp)}}o
      # maybe homebrew is installed to /sw or /opt/brew
      true
    when %r{^/opt}, %r{^/sw}, %r{/usr/X11}
      false
    else
      true
    end
  end

  def cflags
    args = []

    return args unless cccfg? 'O' or configure?

    args << '-pipe'
    args << '-w' unless configure?
    args.concat(optflags)
    args.concat(archflags)
    args << "-std=#{@arg0}" if @arg0 =~ /c[89]9/
    args
  end

  def cxxflags
    args = cflags
    args << '-std=c++11' if cccfg? 'x'
    args << '-stdlib=libc++' if cccfg? 'g'
    args << '-stdlib=libstdc++' if cccfg? 'h'
    args
  end

  def optflags
    args = []
    args << "-#{ENV['HOMEBREW_OPTIMIZATION_LEVEL']}"
    args.concat ENV['HOMEBREW_OPTFLAGS'].split(' ') if ENV['HOMEBREW_OPTFLAGS']
    args
  end

  def archflags
    ENV["HOMEBREW_ARCHFLAGS"].split(" ")
  end

  def cppflags
    path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths)
  end

  def ldflags
    args = path_flags("-L", library_paths)
    case mode
    when :ld
      args << "-headerpad_max_install_names"
    when :ccld, :cxxld
      args << "-Wl,-headerpad_max_install_names"
    end
    args
  end

  def isystem_paths
    path_split("HOMEBREW_ISYSTEM_PATHS")
  end

  def include_paths
    path_split("HOMEBREW_INCLUDE_PATHS")
  end

  def library_paths
    path_split("HOMEBREW_LIBRARY_PATHS")
  end

  def system_library_paths
    %W{#{sysroot}/usr/lib /usr/local/lib}
  end

  def make_fuss args
    return unless make_fuss?

    dels = @args - args
    adds = args - @args

    LOGGER.puts "superenv removed:  #{dels*' '}" unless dels.empty?
    LOGGER.puts "superenv added:    #{adds*' '}" unless adds.empty?
  end

  def make_fuss?
    cccfg? 'O' and not configure?
  end

  def configure?
    # configure scripts generated with autoconf 2.61 or later export as_nl
    ENV.key? 'as_nl'
  end

  def cccfg? flags
    flags.split('').all?{|c| ENV['HOMEBREW_CCCFG'].include? c } if ENV['HOMEBREW_CCCFG']
  end

  def canonical_path(path)
    path = Pathname.new(path)
    path = path.realpath if path.exist?
    path.to_s
  end

  def path_flags(prefix, paths)
    paths = paths.uniq.select { |path| File.directory?(path) }
    paths.map! { |path| prefix + path }
  end

  def path_split(key)
    ENV.fetch(key) { "" }.split(File::PATH_SEPARATOR)
  end

  def chuzzle(val)
    return val if val.nil?
    val = val.chomp
    return val unless val.empty?
  end
end

if __FILE__ == $PROGRAM_NAME
  ##################################################################### sanity
  abort "The build-tool has reset ENV. --env=std required." unless ENV['HOMEBREW_BREW_FILE']

  if (cc = ENV["HOMEBREW_CC"]).nil? || cc.empty? || cc == "cc"
    # those values are not allowed
    ENV['HOMEBREW_CC'] = 'clang'
  end

  ####################################################################### main

  LOGGER.puts "#{File.basename($0)} called with: #{ARGV.join(" ")}"

  cmd = Cmd.new($0, ARGV)
  tool, args = cmd.tool, cmd.args

  LOGGER.puts "superenv executed: #{tool} #{args.join(" ")}"
  LOGGER.log!

  args << { :close_others => false } if RUBY_VERSION >= "2.0"
  exec "#{File.dirname($0)}/xcrun", tool, *args
end
